#ifndef ATTACK_HPP
#define ATTACK_HPP

#include "army.hpp"

#include <map>
#include <set>
#include <list>
class attack
  : public army
{
  /*
  int _group;
  
  double _attackradius;
  

  const std::vector<item>* _myantlist;
  */
  int _turn;
  std::ofstream f;
  const bitmap* _water;
  const bitmap* _myants;
  igeneral* _g;
  double _attackradius;
public:
  
  bool check(location loc) const
  {
    return !(_water->get(loc) || _myants->get(loc));
  }
  attack()
  {
    f.open("attack.txt");
  }

  // std::set<advice> _result;

  virtual advice candidate()
  {
    return advice();
    /*
    if ( _result.empty() )
      return advice();

    f << "candidate (" << _result.begin()->row << "," << _result.begin()->col << "): " << _result.begin()->direction << std::endl;
    return *(_result.begin());
    */
  }

  virtual void committed( location from, location to)
  {
    /*
    if ( _result.empty() )
      return;
    f << _result.size() << " erase (" << from.row << "," << from.col << ")" << std::endl;
    _result.erase( advice(from) );
    */
  }

  // Находим своих которые в ближайший ход могут войти в бой
  void make_battlelist(std::list<item>& battlelist)
  {
    const bitmap& attack = _g->enemyattack(2);
    const std::vector<item>& myantslist = _g->myantlist();

    std::vector<item>::const_iterator beg = myantslist.begin();
    std::vector<item>::const_iterator end = myantslist.end();
    for (;beg!=end; ++beg)
    {
      if ( attack.get( *beg ) )
        battlelist.push_back( *beg );
    }

    {{

      if (battlelist.empty())
      {
        f << "NO BATTLE " << std::endl;
        f.flush();
        return;
      }
      else
      {
        f << "MY IN BATTLE " << battlelist.size() << std::endl;
        f.flush();
      }

    }}
  }

  // Добавляем врагов с кем возможно сражение
  void retrofitted_battlelist(std::list<item>& battlelist)
  {
    size_t s = battlelist.size();
    double attackradius2 = _g->attackradius() + 2;
    const std::vector<item>& enemyantslist = _g->enemyantlist();

    // Перебираем всех врагов
    std::vector<item>::const_iterator beg = enemyantslist.begin();
    std::vector<item>::const_iterator end = enemyantslist.end();
    for (;beg!=end; ++beg)
    {
      f.flush();
      std::list<item>::iterator itr = battlelist.begin();

      for ( size_t i = 0; i!=s; ++itr, ++i)
      {
        if ( location::distance(*beg, *itr, _area) <= attackradius2 )
        {
          f << "(" << beg->row << "," << beg->col << "->" << itr->row << "," << itr->col<< "), ";
          battlelist.push_back( *beg );
          // Один может двоих атаковать
          break;
        }
      }
    }

    {{
      f << battlelist.size();
      f << std::endl;
      if (  s == battlelist.size() )
      {
          f << "BATTLE WITHOUT ANEMY?? " << std::endl;
          f.flush();
          return;
      }
      else
        f << "YO! BATTLE " << battlelist.size() << std::endl;
      f.flush();
    }}
  }

  /// Разбиваем на группы
  void group_divisions(std::list<item> battlelist, std::map< int, std::vector<advice> >& battlemap)
  {
    typedef std::vector<advice> advice_list;
    typedef std::map< int, std::vector<advice> > battlemap_type;

    int battlesize = battlelist.size();
    double attackradius2 = _g->attackradius() + 2;
    
    for (int gr = 0; battlesize!=0 && !battlelist.empty(); gr++)
    {
      advice_list& advlist = battlemap.insert( std::make_pair(gr, advice_list() ) ).first->second;
      
      f << "battlesize=" << battlesize << std::endl;
      f << "GROUP "<<gr << ": " ;
      // Берем первого попавшегося
      advice adv( location( battlelist.front() ));
      adv.value = battlelist.front().value;
      battlelist.erase(battlelist.begin());
      advlist.push_back(adv);
      --battlesize;
      f << adv.value << "(" << adv.row << "," << adv.col << "), ";
      
      int flag = 1;
      for(; flag && advlist.size() < 9 && !battlelist.empty();)
      {
        f << "for1 battlesize=" << battlesize << "advlist.size()="<< advlist.size()<< std::endl;
        flag = 0;
        // 2.2 Ближайшего врага
        {
          std::list<item>::iterator beg =  battlelist.begin();
          std::advance(beg, battlesize);
          std::list<item>::iterator end =  battlelist.end();
          if (beg!=end)
          {
            
            std::list<item>::iterator cur = beg;
            //item enemy = *(beg++);
            double dist = location::distance(adv, *cur, _area);
            for (;beg!=end;++beg)
            {
              double dist_cur = location::distance(adv, *beg, _area);
              if (  dist_cur < dist )
              {
                cur = beg;
                dist = dist_cur;
              }
            }
            
            if ( dist < attackradius2 * 3 )
            {
              f << cur->value << "(" << cur->row << "," << cur->col << "), ";
              advlist.push_back(*cur);
              battlelist.erase(cur);
              ++flag;
            }
          }
        }
        
        if ( advlist.size() >= 8 ) break;
        f << "for2 battlesize=" << battlesize << "advlist.size()="<< advlist.size()<< std::endl;
        // 2.2 Ближайшего друга
        {
          std::list<item>::iterator beg =  battlelist.begin();
          std::list<item>::iterator end =  battlelist.begin();
          std::advance(end, battlesize);
          if (beg!=end)
          {
            
            std::list<item>::iterator cur = beg;
            
            double dist = location::distance(adv, *cur, _area);
            for (;beg!=end;++beg)
            {
              double dist_cur = location::distance(adv, *beg, _area);
              if (  dist_cur < dist )
              {
                cur = beg;
                dist = dist_cur;
              }
            }
            if ( dist < attackradius2 * 3 )
            {
              f << cur->value << "(" << cur->row << "," << cur->col << "), ";
              advlist.push_back(*cur);
              battlelist.erase(cur);
              --battlesize;
              ++flag;
            }
          }
        }
      }
      f << std::endl;
    }

    f << "GROUPS " << battlemap.size() << std::endl;
  }

  void alignment_of_forces(std::map< int, std::vector<advice> >& battlemap)
  {
    typedef std::vector<advice> advice_list;
    typedef std::map< int, std::vector<advice> > battlemap_type;

    battlemap_type::iterator beg = battlemap.begin();
    battlemap_type::iterator end = battlemap.end();
    for ( ;beg!=end; ++beg)
    {
      f << "beg->second.size() == " << beg->second.size() << std::endl;;
      dobattle( beg->second );
    }
  }

  void fix_result( std::map< int, std::vector<advice> >& battlemap, std::vector<advice>& result )
  {
    typedef std::vector<advice> advice_list;
    typedef std::map< int, std::vector<advice> > battlemap_type;

    battlemap_type::iterator beg = battlemap.begin();
    battlemap_type::iterator end = battlemap.end();
    for ( ;beg!=end; ++beg)
    {
      advice_list::iterator abeg = beg->second.begin();
      advice_list::iterator aend = beg->second.end();
      for ( ;abeg!=aend; ++abeg)
      {
        if ( abeg->value == 0)
          result.push_back( *abeg );
      }
    }

    f << "RESULT " << result.size();
    {
      std::vector<advice>::iterator beg = result.begin();
      std::vector<advice>::iterator end = result.end();
      for ( ;beg!=end;++beg)
        f<< "(" << beg->row << "," << beg->col << "," << beg->direction << "," << beg->weight <<");";
      f << std::endl;
    }
  }

  void commit( std::vector<advice>& result )
  {
    std::vector<advice>& forces = _g->forces();
    std::vector<advice>::iterator beg = result.begin();
    std::vector<advice>::iterator end = result.end();
    for ( ;beg!=end ;++beg)
    {
      std::vector<advice>::iterator itr = std::lower_bound(forces.begin(), forces.end(), *beg);
      *itr = *beg;
    }
  }

  virtual void reconnaissance( igeneral* g )
  {
    _g = g;
    _water = &(_g->water());
    _myants = &(_g->myants());
    _turn = _g->turn();
    _attackradius = _g->attackradius();

    f << "TURN " << _turn << std::endl;
    f.flush();

    // 1. Выгребаем наших которые попадают под атаку
    std::list<item> battlelist;
    f << "make_battlelist..." << std::endl;
    this->make_battlelist(battlelist);
    if (battlelist.empty()) return;
    size_t battlesize = battlelist.size();

    f << "retrofitted_battlelist..." << std::endl;
    // 2. Выбираем врагов учавствующих в атаке
    retrofitted_battlelist(battlelist);
    if ( battlesize == battlelist.size() )
      return;

    f << "group_divisions..." << std::endl;
    // 3. Разбиваем на группы
    typedef std::vector<advice> advice_list;
    typedef std::map< int, std::vector<advice> > battlemap_type;
    battlemap_type battlemap;
    group_divisions(battlelist, battlemap);

    // 4. Определяем лучшие ходы
    f << "alignment_of_forces..." << std::endl;
    alignment_of_forces(battlemap);

    f << "fix_result..." << std::endl;
    // 5. фиксируем результаты
    std::vector<advice> result;
    fix_result(battlemap, result);

    f << "commit..." << std::endl;
    // 6. Ну и наконец передаем их генералу
    commit( result );
    f << "attack ready" << std::endl;
  }


  int dobattle(std::vector<advice>& income, int rec = 0)
  {
    if ( income.size() < 2 ) // это недостаток алгоритма
      return 0;

    std::vector<advice> current(income.begin(), income.end());
    int mycount = 1;
    int enemycount = 1;
    std::vector<advice>::iterator itr = current.begin();
    f << "BATTLE ";
    for ( ;itr!=current.end();++itr)
    {
      f << "(" << itr->row << "," << itr->col << "), ";
     if (itr->value == 0)
        mycount *= 5;
      else
        enemycount *= 5;
    }
    int count = mycount * enemycount;
    f << " count=" << count;
    f << std::endl;
    // std::cerr << "dobattle count "<< count << std::endl;
    int maxweight = -2500;
    int minweight = 2500;
    std::vector<advice> maxadvs;
    std::vector<advice> minadvs;
    for (int i = 0; i < mycount /** enemycount*/; ++i)
    {
      // int iw = battlestep(i, current);
      int iw = battlestep_my(i, enemycount, current);
      if ( iw == -2500 )
        continue;

      if ( /*maxweight*/iw == 0 && rec == 0 && income.size() < 4)
        iw = dobattle(current, 1);
      if ( iw > maxweight)
      {
        maxweight = iw;
        maxadvs = current;
      }
      if ( iw < minweight)
      {
        minweight = iw;
        minadvs = current;
      }
    }

    income = maxadvs;

    if ( maxweight!=0 || minweight!=0)
    {
      f << _turn << " WEIGHT " << std::endl;

      if ( maxweight!=-2500)
      {
        f << "\tMAX " << maxweight << " -> ";
        std::vector<advice>::iterator itr = maxadvs.begin();
        for ( ;itr!=maxadvs.end();++itr)
          f << itr->direction << "(" << itr->row << "," << itr->col << ")" ;
        f << std::endl;
      }

      if ( minweight!=2500)
      {
        f << "\tMIN "<< minweight << " -> ";
        std::vector<advice>::iterator itr = minadvs.begin();
        for ( ;itr!=minadvs.end();++itr)
          f << itr->direction << "(" << itr->row << "," << itr->col << "), ";
        f << std::endl;
      }
    }
    // f << _turn << " WEIGHT " << weight << " attackradius "<< _attackradius << std::endl;
    return maxweight;
  }
  
  int battlestep_my(int step, int enemycount, std::vector<advice>& income)
  {
    for ( int i=0; i < (int)income.size(); ++i )
    {
      if ( income[i].value == 0)
      {
        int d = step % 5;
        if (d == 4) d = -1;
        income[i].direction = d;
        step /= 5;
        if ( !check(location::move( income[i], d, _area)))
          return -2500;
      }
    }

    // Высчитываем худший и лучший вес
    int worse = 2500;
    int best = -2500;
    for (int j=0; j < enemycount; ++j)
    {
      bool flag = true;
      for ( int i=0; i < (int)income.size(); ++i )
      {
        if ( income[i].value != 0)
        {
          int d = j % 5;
          if (d == 4) d = -1;
          income[i].direction = d;
          step /= 5;
          if ( !check(location::move( income[i], d, _area)))
          {
            flag = false;
            break;
          }
        }
      }
      if ( !flag )
        continue;

      int w = calcstep( income );
      if ( w < worse)
        worse = w;
      if ( w > best)
        best = w;
    }
    
    if ( worse > -1)
      return best;
    return worse;

  }

  int battlestep(int step, std::vector<advice>& income)
  {

    // std::cerr << "step " << step << " ";
    
    for ( int i=0; i < (int)income.size(); ++i )
    {
      int d = step % 5;
      if (d == 4) d = -1;
      income[i].direction = d;
      step /= 5;
    }
    // std::cerr << std::endl;
    int weight = calcstep( income );

    //std::cerr << "WEIGHT " << weight << std::endl;
    if ( weight!=0)
    {
      // f << _turn << " WEIGHT " << weight << std::endl;
      //throw std::logic_error("yes!!");
    }
    return weight;
  }

  int calcstep( std::vector<advice>& income )
  {
    //std::vector<location> check(income.size());
    int w = 0;
    std::vector<advice>::iterator beg = income.begin();
    std::vector<advice>::iterator end = income.end();
    for ( int i = 0; beg!=end; ++beg, ++i)
      beg->weight = 0;
    beg = income.begin();
    for ( /*--end*/; beg!=end; ++beg)
    {
      std::vector<advice>::iterator ibeg = beg;
      std::vector<advice>::iterator iend = end;
      /*++ibeg;
      ++iend;
      */
      for (;ibeg!=iend;++ibeg)
      {
        if (beg==ibeg)
          continue;

        location m1 = location::move( *beg, beg->direction, _area);
        location m2 = location::move( *ibeg, ibeg->direction, _area);
        if ( m1 == m2 && beg->value == 0)
        {
          beg->weight+=1;
          ibeg->weight+=1;
          continue;
        }

        if ( ibeg->value == beg->value)
          continue;

        if ( location::distance( m1, m2, _area) <= _attackradius )
        {
          //if ( beg->weight == ibeg->weight )
          {
            beg->weight += 1;
            ibeg->weight += 1;
/*            beg->weight = beg->weight*2 + 1;
            ibeg->weight = ibeg->weight*2 + 1;*/
          }
          /*else if ( beg->weight < ibeg->weight )
          {
            ibeg->weight+=1;
          }
          else
          {
            beg->weight+=1;
          }*/
          /*
            beg->weight+=1;
            ibeg->weight+=1;
          */
          // f << "*";
          // Не важно наши или враги
/*          if ( beg->value != ibeg->value)
          {
            //  std::cerr << "! " << beg->value << "!= " <<ibeg->value << " W " << beg->weight << " " << ibeg->weight<< std::endl;
            beg->weight+=1;
            ibeg->weight+=1;
          }
            */
        }
      }
    }
    
    beg = income.begin();
    end = income.end();
    //std::cerr << "W " ;
    int total = 0;
    for ( ; beg!=end; ++beg)
    {
      if ( beg->value == 0 )
        total += beg->weight;

      //f << beg->value << "(" << beg->weight << ") " ;
      /*if ( beg->weight > 1 || beg->value == 0 )
        beg->weight *= 2;*/
      if ( beg->weight > 1)
      {
        // йо! когото замочили
        w += ( beg->value == 0) ? -4 : +2;
      }
      else if ( beg->weight == 1)
      {
        w += ( beg->value == 0) ? -2 : +0;
      }
      else if ( beg->weight == 0)
      {
        w += ( beg->value == 0) ? -0 : +0;
      }
      /*
      if ( beg->value == 0)
        w -= beg->weight;
      else
        w += beg->weight;
      */
    }
    //f << w << std::endl;
  /*  if ( w == 0 && total!=0)
      --w;*/
    return w;
  }
  
  void show(std::ostream& os)
  {
    os << "ATTAK not impl" <<  std::endl;
  }

};

#endif
